[Go] Slice(切片)
前言
Slice
是在一個陣列中的一個區段,與陣列一樣,slice
可透過索引的方式存取,同時也具有長度。但與陣列不同的是,**slice
長度是可以改變的**,若只想處理陣列中某片區域可以使用slice
。
slice
在中括號[]
之間沒有表示長度的數字slice
底層實際上還是個陣列- 參考的預設值都是
nil
- slice 唯一可以用
==
比較的對象為nil
,儲存slice
參考的變數也無法進行==
比較
Slice 基礎用法
宣告 slice
slice宣告方法有兩種:
- 像array一樣宣告, 不須指定 slice 大小
- 使用內建函數make
像 array 一樣宣告
1 |
|
使用內建函數 make
除了上述方法外, Golang 提供了一個名為 make()
的函數來建立 slice。 它分配 長度與給定容量相同的底層陣列,並返回參考該陣列的 slice。
語法結構:
1 |
|
參數說明:
T
: 該 slice 存放的資料類型len
: 該 slice 的長度cap
: 該 slice 的容量,此參數是非必要
的。若省略,則預設為指定的長度
範例:
有指定 slice 的容量:
1 |
|
沒有指定 slice 的容量:
1 |
|
Slice 的零值
slice 的零值為 nil
。帶有零值的 slice 沒有任何底層陣列,且長度和容量為 0
1 |
|
常見用法
re-slicing 重新切片
使用冒號間隔兩個參數,此方式可擷取 slice 特定範圍的值。
語法結構
1 |
|
說明
start
: 起始位置,預設值為0
end
: 終點,預設值為該 slice 的長度(len(slice)
)
需要注意的是:
[start:end]
取出的範圍是start
到end - 1
的範圍start
,end
值可不給,如:[:]
1 |
|
重新切片後的 slice 底層陣列不變,僅改變指標位址
觀察到上述重新切片後的 slice 底層陣列不變的情況後,若不想兩個 slice 共用同一個陣列,則要使用另一個內建函數 copy()
拷貝: copy()
copy()
將一個 slice 的內容,複製至另一個 slice (複製 slice 中原始指向的底層陣列至另一個新的 slice ,且新slice 指向新陣列
語法結構
1 |
|
結合 re-slicing 指定要複製的位置
1 |
|
注意以下情況
len(src-slice)
<len(dst-slice)
時: 會覆蓋dst-slice
的前len(src-slice)
個數1
2
3
4
5
6x := []int{1, 2, 3, 4, 5}
y := []int{6, 7, 8}
copy(x, y) // 把y複製進x
fmt.Println("x:", x, "y: ", y) // x: [6 7 8 4 5] y: [6 7 8]
y[2] = 0
fmt.Println("x:", x, "y: ", y) // [1 2]len(src-slice)
>len(dst-slice)
時: 複製src-slice
與dst-slice
等長的值1
2
3
4
5
6x := []int{1, 2, 3, 4, 5}
y := []int{6, 7, 8}
copy(y, x) // 把 x 複製進 y
fmt.Println("x:", x, "y: ", y) // x: [1 2 3 4 5] y: [1 2 3]
y[2] = 0
fmt.Println("x:", x, "y: ", y) // x: [1 2 3 4 5] y: [1 2 0]
擴增: append()
在給定的 slice 尾端擴增的新元素,回傳一個新的 slice
若給定的 Slice 無足夠的容量容納這些新元素,則會新配發具有更大容量的底層陣列。原有的 slice 底層陣列中所有元素都會被複製到新陣列內,再添加新元素。
反之,若給定的 slice 有足夠容量來容納新元素,則使用其底層陣列並將新元素附加到同一陣列中。
語法結構
1 |
|
範例
擴增單一元素
1 |
|
ellipsis: 將一個 Slice 添加到另一個 slice
使用運算子: ...
將一個 slice 直接添加至另一 slice
1 |
|
slice 中放 slice
我們亦可於 slice 中放另外一個 slice,類似二維的概念
1 |
|
slice literals
可用與迭代陣列相同的方式對 slice 做迭代。
- for-loop 的形式 (without
range
)1
2
3for i := 0; i < len(numb); i++ {
fmt.Println(numb[i])
} - for-loop 的形式 (with
range
)1
2
3for i, v := range numb {
fmt.Println(i, v)
}
需注意之處
slice 底層有個指標指向一個 array
slice 本身是一個引用型別,底層會有個指標指向一個array
slice 的長度 == slice 中元素的數量
slice 的長度是 slice 中元素的數量
slice 為 nil 時,不會於 loop 中的 range 執行
1 |
|
上述範例會發現宣告 games 為 slice 型別後,初始值為 nil
,嘗試用迴圈讀取裡面的值,發現沒有印出任何東西,程式仍能夠正常運作。
空的 Slice 值等同於 nil
,來看下述範例:
1 |
|
執行的結果為 true
,表兩者同等。
array v.s slice 差異
array
- 固定長度,長度在編譯時期(complie time)已經建立
- array 長度
屬於型別
的一部分 - 宣告後無法改變陣列長度
- 宣告 array 內的元素型別為
int
且未給值時,array 內的元素值為0
- array 在
長度相同
時可進行比較 - array 在
長度相同
時可進行賦值(assign:=
)操作
slice
- 長度可變動
- 長度
不屬於型別
的一部分 - 長度於執行期(runtime)才變動
- 宣告 slice 而未給值時,slice 內的值為
nil
- slice 只可對
nil
值做比較(比較的對象是nil
) - slice 在元素
型別相同
時即可進行賦值(assign:=
)操作,跟長度無關
範例
1 |
|